/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2017 NXP
 * All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY NXP "AS IS" AND ANY EXPRESSED OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL NXP OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @page misra_violations MISRA-C:2012 violations
 *
 * @section [global]
 * Violates MISRA 2012 Required Rule 1.3, Taking address of near auto variable.
 * The code is not dynamically linked. An absolute stack address is obtained
 * when taking the address of the near auto variable. A source of error in
 * writing dynamic code is that the stack segment may be different from the data
 * segment.
 *
 * @section [global]
 * Violates MISRA 2012 Required Rule 10.3, Expression assigned to a narrower or different essential type
 * The cast is used to convert from uint32_t to enum_type.
 *
 * @section [global]
 * Violates MISRA 2012 Advisory Rule 10.5, Impermissible cast; cannot cast from 'essentially unsigned' to 'essentially enum<i>'
 * The cast is used to convert from uint32_t to enum_type.
 *
 * @section [global]
 * Violates MISRA 2012 Advisory Rule 11.4, Conversion between a pointer and integer type.
 * The cast is required to initialize a pointer with an unsigned long define, representing an address.
 *
 * @section [global]
 * Violates MISRA 2012 Required Rule 11.6, Cast from unsigned int to pointer.
 * The cast is required to initialize a pointer with an unsigned long define, representing an address.
 *
 * @section [global]
 * Violates MISRA 2012 Advisory Rule 8.7, External could be made static.
 * Function is defined for usage by application code.
 *
 */

#include "lptmr_driver.h"
#include "lptmr_hw_access.h"
#include "clock_manager.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
/* Takes into consideration that LPTMR compare events take place
 * when "the CNR equals the value of the CMR and increments" */
#define LPTMR_MAX_CMR_NTICKS (LPTMR_CMR_COMPARE_MASK + 1u)
#define LPTMR_MAX_PRESCALER  (1u << LPTMR_PSR_PRESCALE_WIDTH)

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*! @cond DRIVER_INTERNAL_USE_ONLY */

/* Table of base addresses for LPTMR instances */
static LPTMR_Type* const g_lptmrBase[LPTMR_INSTANCE_COUNT] = LPTMR_BASE_PTRS;

/*******************************************************************************
 * Private Functions
 ******************************************************************************/
static inline uint8_t lptmr_cfg2p(const lptmr_prescaler_t prescval,
		const bool bypass);

static inline uint64_t lptmr_us2nn(const uint32_t clkfreq, const uint32_t us);

static inline uint64_t lptmr_compute_nticks(uint64_t nn, uint8_t p);

static inline bool nticks2compare_ticks(uint64_t nticks, uint16_t* ticks);

static uint32_t lptmr_GetClkFreq(const lptmr_clocksource_t clkSrc,
		const uint32_t instance);

static bool lptmr_Ticks2Us(const uint32_t clkfreq, const lptmr_prescaler_t pval,
		const bool bypass, const uint16_t ticks, uint32_t* const us);

static bool lptmr_Us2Ticks(const uint32_t clkfreq,
		const lptmr_prescaler_t prescval, const bool bypass, const uint32_t us,
		uint16_t* const ticks);

static bool lptmr_ChooseClkConfig(const uint32_t clkfreq, const uint32_t us,
		lptmr_prescaler_t* const prescval,
		bool* const bypass, uint16_t* const ticks);

/*TIMER MODE CONFIGURATION******************************************************
 *
 * Timer Mode - Prescaler settings calculations
 * --------------------------------------------
 *
 * Timer Mode configuration takes a period (timeout) value expressed in
 * micro-seconds. To convert this to LPTMR prescaler (and compare value)
 * settings, the closest match must be found.
 * For best precision, the lowest prescaler that allows the corresponding
 * compare value to fit in the 16-bit register will be chosen.
 *
 * Algorithm for choosing prescaler and compare values:
 * =============================================================================
 * In: tper_us (period in microseconds), fclk (input clock frequency in Hertz)
 * Out: nticks (timer ticks), p (prescaler coefficient, 2^p = prescaler value)
 * ---
 * 1) Compute nn = tper_us * fclk / 1000000
 * 2) for p = 0..16
 *  2.1) nticks = nn / 2^p
 *  2.2) if nticks < 0x10000
 *      2.2.1) STOP, found nticks and p
 * 3) nticks = 0xFFFF, p = 16
 * =============================================================================
 *
 * A few names used throughout the static functions affecting Timer mode:
 *  nn - total number of timer ticks (undivided, unprescaled) that is necessary
 *      for a particular timeout.
 *      nn = (tper_us * fclk) / 1000000 = nticks * npresc
 *
 *  tper_us - a period (or timeout) expressed in microsecond units. In most
 *      functions will be denoted as 'us' for microseconds.
 *
 *  nticks - number of timer ticks that is necessary for a particular timeout,
 *      after prescaling
 *
 *  npresc - prescaler value (1, 2, 4 ... 65536)
 *
 *  p - prescaler coefficient, 2^p = npresc
 *
 *  fclk - input clock frequency, in Hertz. In most function will be denoted as
 *      'clkfreq'.
 *END**************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : lptmr_cfg2p
 * Description   : Transform prescaler settings (bypass on/off, prescaler value)
 *  to prescaler coefficient value (2's power), p.
 * Return: the value of p.
 *END**************************************************************************/
static inline uint8_t lptmr_cfg2p(const lptmr_prescaler_t prescval,
		const bool bypass) {
	uint8_t p = 0u;

	if (!bypass) {
		p = (uint8_t) (((uint8_t) prescval) + 1u);
	}

	return p;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lptmr_us2nn
 * Description   : Transform microseconds to undivided (unprescaled) timer units,
 * nn.
 * Return: the value of nn.
 *END**************************************************************************/
static inline uint64_t lptmr_us2nn(const uint32_t clkfreq, const uint32_t us) {
	/* Approximate the timeout in undivided (unprescaled) timer ticks.
	 - us is the timeout in microseconds (1/10^6 seconds)
	 - clkfreq is the frequency in Hertz
	 Operation:
	 nn = (us/1000000) * clkfreq
	 In C:
	 For better precision, first to the multiplication (us * clkfreq)
	 To overcome the truncation of the div operator in C, add half of the
	 denominator before the division. Hence:
	 nn = (us * clkfreq + 500000) / 1000000
	 */
	/* There is no risk of overflow since us is 32-bit wide and clkfreq can be
	 a theoretical maximum of ~100 MHz (platform maximum), which is over the
	 maximum input of the LPTMR anyway
	 */
	uint64_t nn = (uint64_t) ((uint64_t) us * (uint64_t) clkfreq);
	nn = (nn + 500000u) / 1000000u;
	return nn;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lptmr_compute_nticks
 * Description   : Compute total number of divided (prescaled) timer ticks,
 * nticks.
 * Return: the value of nticks.
 *END**************************************************************************/
static inline uint64_t lptmr_compute_nticks(uint64_t nn, uint8_t p) {
	uint64_t npresc = (uint64_t) 1u << p;
	DEV_ASSERT(npresc != 0u);

	/* integer division */
	uint64_t nticks = ((nn + (npresc >> 1u)) / npresc);

	return nticks;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : nticks2compare_ticks
 * Description   : Transform the value of divided (prescaled) timer ticks, nticks
 * to a 16-bit value to be written to the hardware register. Cap or underflow
 * cause an error.
 * Return: the success state.
 *  - true: no underflow or overflow detected
 *  - false: value written was capped, underflow or overflow detected
 *
 *END**************************************************************************/
static inline bool nticks2compare_ticks(uint64_t nticks, uint16_t* ticks) {
	bool success = true;

	/* if nticks fits, write the value to ticks */
	if (nticks <= LPTMR_MAX_CMR_NTICKS) {
		if (nticks == 0u) {
			/* timeout period (us) too low for prescaler settings */
			*ticks = 0u;
			success = false;
		} else {
			/* According to RM, the LPTMR compare events take place when "the CNR equals the value of the CMR and increments".
			 * The additional increment is compensated here by decrementing the calculated compare value with 1, before being written to CMR. */
			*ticks = (uint16_t) (nticks - 1u);
		}
	} else {
		/* timeout period (us) too high for prescaler settings */
		*ticks = LPTMR_CMR_COMPARE_MASK;
		success = false;
	}

	return success;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lptmr_GetClkFreq
 * Description   : Get the clock frequency for the selected clock source. If the
 * selected clock source is not enabled, a frequency of 0 is returned.
 * Return values:
 *  - the clock frequency or 0 if the clock is invalid.
 *
 *END**************************************************************************/
static uint32_t lptmr_GetClkFreq(const lptmr_clocksource_t clkSrc,
		const uint32_t instance) {
	/* LPTMR PCC clock source names, for getting the input clock frequency */
	static const clock_names_t lptmrPccClockName[LPTMR_INSTANCE_COUNT] = {
			LPTMR0_CLK };
	clock_names_t inputClockName = SIRC_CLK;
	uint32_t clkFreq;
	status_t clkStatus;

	/* Get input clock name */
	switch (clkSrc) {
	case LPTMR_CLOCKSOURCE_SIRCDIV2:
		inputClockName = SIRC_CLK;
		break;
	case LPTMR_CLOCKSOURCE_1KHZ_LPO:
		inputClockName = SIM_LPO_1K_CLK;
		break;
	case LPTMR_CLOCKSOURCE_RTC:
		inputClockName = SIM_RTCCLK_CLK;
		break;
	case LPTMR_CLOCKSOURCE_PCC:
		inputClockName = lptmrPccClockName[instance];
		break;
	default:
		/* Invalid clock source */
		DEV_ASSERT(false);
		break;
	}

	/* Get input clock frequency */
	if (inputClockName == SIRC_CLK) {
		clkStatus = CLOCK_SYS_GetFreq(SIRCDIV2_CLK, &clkFreq);
		DEV_ASSERT(clkStatus == STATUS_SUCCESS);
		(void) clkStatus;
		DEV_ASSERT(clkFreq != 0u); /* If the GetFreq functions fails, clkfreq will be 0 */
	} else {
		clkStatus = CLOCK_SYS_GetFreq(inputClockName, &clkFreq);
		DEV_ASSERT(clkStatus == STATUS_SUCCESS);
		(void) clkStatus;
		DEV_ASSERT(clkFreq != 0u); /* If the GetFreq functions fails, clkfreq will be 0 */
	}

	return clkFreq;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lptmr_Ticks2Us
 * Description   : Transform timer ticks to microseconds using the given
 * prescaler settings. Clock frequency must be valid (different from 0).
 * Possible return values:
 * - true: conversion success
 * - false: conversion failed, result did not fit in 32-bit.
 *
 *END**************************************************************************/
static bool lptmr_Ticks2Us(const uint32_t clkfreq, const lptmr_prescaler_t pval,
		const bool bypass, const uint16_t ticks, uint32_t* const us) {
	bool success = true;
	uint8_t p = lptmr_cfg2p(pval, bypass);
	uint64_t nn = ((uint64_t) ticks + 1u) << p;
	uint64_t us_real = (nn * 1000000u) / (clkfreq);
	uint32_t us_local;

	if (us_real <= (0xFFFFFFFFu)) {
		us_local = (uint32_t) us_real;
	} else {
		us_local = 0xFFFFFFFFu;
		success = false;
	}

	*us = us_local;
	return success;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lptmr_Us2Ticks
 * Description   : Transform microseconds to timer ticks using the given
 * prescaler settings. Input clock frequency, clkfreq, must be greater than 0.
 * Possible return values:
 * - true: conversion completed successfully
 * - false: conversion failed, value did not fit in 16-bit.
 *
 *END**************************************************************************/
static bool lptmr_Us2Ticks(const uint32_t clkfreq,
		const lptmr_prescaler_t prescval, const bool bypass, const uint32_t us,
		uint16_t* const ticks) {
	bool success = true;
	/* Transform prescaler configuration to prescaler coefficient p */
	uint8_t p = lptmr_cfg2p(prescval, bypass);
	/* Compute nn, the number of ticks necessary for the period in microseconds
	 without any prescaler */
	uint64_t nn = lptmr_us2nn(clkfreq, us);
	/* Compute nticks, total number of ticks with prescaler */
	uint64_t nticks = lptmr_compute_nticks(nn, p);
	/* Transform nticks to value to be written to register */
	success = nticks2compare_ticks(nticks, ticks);
	return success;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lptmr_ChooseClkConfig
 * Description   : Choose clocking configuration (prescaler value, timer ticks)
 * for the desired timeout period, given in microseconds. Input clock frequency,
 * clkfreq, must be greater than 0.
 * Possible return values:
 * - true: configuration found
 * - false: configuration mismatch, desired timeout period is too small or too
 * big for the clock settings.
 *
 *END**************************************************************************/
static bool lptmr_ChooseClkConfig(const uint32_t clkfreq, const uint32_t us,
		lptmr_prescaler_t* const prescval,
		bool* const bypass, uint16_t* const ticks) {
	uint8_t p;
	uint64_t nticks = 0ULL;
	bool success;

	uint64_t nn = lptmr_us2nn(clkfreq, us);

	/* Find the lowest prescaler value that allows the compare value in 16-bits */
	for (p = 0u; p <= LPTMR_MAX_PRESCALER; p++) {
		nticks = lptmr_compute_nticks(nn, p);

		if (nticks <= LPTMR_MAX_CMR_NTICKS) {
			/* Search finished, value will fit in the 16-bit register */
			break;
		}
	}

	success = nticks2compare_ticks(nticks, ticks);

	/* Convert p to prescaler configuration */
	if (p == 0u) {
		/* Prescaler value of 1 */
		*bypass = true;
		*prescval = LPTMR_PRESCALE_2;
	} else {
		*bypass = false;
		p--; /* Decrement to match lptmr_prescaler_t.  */
		*prescval = (lptmr_prescaler_t) p;
	}

	return success;
}

/*******************************************************************************
 * Public Functions
 ******************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_InitConfigStruct
 * Description   : Initialize a configuration structure with default values.
 *
 * Implements : LPTMR_DRV_InitConfigStruct_Activity
 *END**************************************************************************/
void LPTMR_DRV_InitConfigStruct(lptmr_config_t * const config) {
	DEV_ASSERT(config != NULL);

	/* General parameters */
	config->dmaRequest = false;
	config->interruptEnable = false;
	config->freeRun = false;
	config->workMode = LPTMR_WORKMODE_TIMER;

	/* Counter parameters */
	config->clockSelect = LPTMR_CLOCKSOURCE_SIRCDIV2;
	config->prescaler = LPTMR_PRESCALE_2;
	config->bypassPrescaler = false;
	config->compareValue = 0u;
	config->counterUnits = LPTMR_COUNTER_UNITS_TICKS;

	/* Pulse Counter specific parameters */
	config->pinSelect = LPTMR_PINSELECT_TRGMUX;
	config->pinPolarity = LPTMR_PINPOLARITY_RISING;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_Init
 * Description   : Initialize a LPTMR instance based on the input configuration
 * structure.
 *
 * When (counterUnits == LPTMR_COUNTER_UNITS_MICROSECONDS) the function will
 * automatically configure the timer for the input compareValue in microseconds.
 * The input parameters for 'prescaler' and 'bypassPrescaler' will be ignored
 * - their values will be adapted by the function, to best fit the input compareValue
 * (in microseconds) for the operating clock frequency.
 *
 * LPTMR_COUNTER_UNITS_MICROSECONDS may only be used for LPTMR_WORKMODE_TIMER mode.
 * Otherwise the function shall not convert 'compareValue' in ticks
 * and this is likely to cause erroneous behavior.
 *
 * When (counterUnits == LPTMR_COUNTER_UNITS_TICKS) the function will use the
 * 'prescaler' and 'bypassPrescaler' provided in the input configuration structure.
 *
 * When (counterUnits == LPTMR_COUNTER_UNITS_TICKS), 'compareValue' must be lower
 * than 0xFFFFu. Only the least significant 16bits of 'compareValue' will be used.
 * When (counterUnits == LPTMR_COUNTER_UNITS_MICROSECONDS), 'compareValue'
 * may take any 32bits unsigned value.
 *
 * Implements : LPTMR_DRV_Init_Activity
 *END**************************************************************************/
void LPTMR_DRV_Init(const uint32_t instance,
		const lptmr_config_t * const config, const bool startCounter) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);
	DEV_ASSERT(config != NULL);

	LPTMR_Type* const base = g_lptmrBase[instance];

	LPTMR_DRV_SetConfig(instance, config);

	/* Start the counter if requested */
	if (startCounter) {
		LPTMR_Enable(base);
	}
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_SetConfig
 * Description   : Configure a LPTMR instance based on the input configuration
 * structure.
 *
 * When (counterUnits == LPTMR_COUNTER_UNITS_MICROSECONDS) the function will
 * automatically configure the timer for the input compareValue in microseconds.
 * The input parameters for 'prescaler' and 'bypassPrescaler' will be ignored
 * - their values will be adapted by the function, to best fit the input compareValue
 * (in microseconds) for the operating clock frequency.
 *
 * LPTMR_COUNTER_UNITS_MICROSECONDS may only be used for LPTMR_WORKMODE_TIMER mode.
 * Otherwise the function shall not convert 'compareValue' in ticks
 * and this is likely to cause erroneous behavior.
 *
 * When (counterUnits == LPTMR_COUNTER_UNITS_TICKS) the function will use the
 * 'prescaler' and 'bypassPrescaler' provided in the input configuration structure.
 *
 * When (counterUnits == LPTMR_COUNTER_UNITS_TICKS), 'compareValue' must be lower
 * than 0xFFFFu. Only the least significant 16bits of 'compareValue' will be used.
 * When (counterUnits == LPTMR_COUNTER_UNITS_MICROSECONDS), 'compareValue'
 * may take any 32bits unsigned value.
 *
 * Implements : LPTMR_DRV_SetConfig_Activity
 *END**************************************************************************/
void LPTMR_DRV_SetConfig(const uint32_t instance,
		const lptmr_config_t * const config) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);
	DEV_ASSERT(config != NULL);

	LPTMR_Type* const base = g_lptmrBase[instance];
	uint32_t configCmpValue = config->compareValue;
	lptmr_workmode_t configWorkMode = config->workMode;
	uint16_t cmpValueTicks = 0U;
	lptmr_prescaler_t prescVal = config->prescaler;
	bool prescBypass = config->bypassPrescaler;
	lptmr_counter_units_t configCounterUnits = config->counterUnits;

	if (configWorkMode == LPTMR_WORKMODE_TIMER) {
		/* A valid clock must be selected when used in Timer Mode. */
		uint32_t clkFreq;
		clkFreq = lptmr_GetClkFreq(config->clockSelect, instance);
		DEV_ASSERT(clkFreq != 0U); /* Clock frequency equal to '0', signals invalid value.  */

		if (configCounterUnits == LPTMR_COUNTER_UNITS_MICROSECONDS) {
			bool chooseClkConfigStatus;

			/* When workmode is set to Timer Mode and compare value is provided in microseconds,
			 * then the input parameters for prescale value and prescaleBypass are ignored.
			 * The prescaleValue, prescaleBypass and cmpValue in ticks, are calculated to best fit
			 * the input configCmpValue (in us) for the current operating clk frequency.  */
			chooseClkConfigStatus = lptmr_ChooseClkConfig(clkFreq,
					configCmpValue, &prescVal, &prescBypass, &cmpValueTicks);
			DEV_ASSERT(chooseClkConfigStatus == true);
			(void) chooseClkConfigStatus;
		} else {
			DEV_ASSERT(configCounterUnits == LPTMR_COUNTER_UNITS_TICKS);
			DEV_ASSERT(configCmpValue <= LPTMR_CMR_COMPARE_MASK); /* Compare Value in Tick Units must fit in CMR. */

			cmpValueTicks =
					(uint16_t) (configCmpValue & LPTMR_CMR_COMPARE_MASK);
		}
	} else {
		/* If configWorkMode is not LPTMR_WORKMODE_TIMER, then it must be LPTMR_WORKMODE_PULSECOUNTER. */
		DEV_ASSERT(configWorkMode == LPTMR_WORKMODE_PULSECOUNTER);

		/* Only LPTMR_COUNTER_UNITS_TICKS can be used when LPTMR is configured as Pulse Counter. */
		DEV_ASSERT(config->counterUnits == LPTMR_COUNTER_UNITS_TICKS);
		/* A valid clock must be selected when glitch filter is enabled (prescaler not bypassed). */
		DEV_ASSERT(
				(lptmr_GetClkFreq(config->clockSelect, instance) != 0u) || prescBypass);
		/* Glitch filter does not support LPTMR_PRESCALE_2. */
		DEV_ASSERT(prescBypass || (prescVal != LPTMR_PRESCALE_2));

		DEV_ASSERT(configCmpValue <= LPTMR_CMR_COMPARE_MASK); /* Compare Value in Tick Units must fit in CMR. */

		cmpValueTicks = (uint16_t) (configCmpValue & LPTMR_CMR_COMPARE_MASK);
	}

	/* Initialize and write configuration parameters. */
	LPTMR_Init(base);

	LPTMR_SetDmaRequest(base, config->dmaRequest);
	LPTMR_SetInterrupt(base, config->interruptEnable);
	LPTMR_SetFreeRunning(base, config->freeRun);
	LPTMR_SetWorkMode(base, configWorkMode);
	LPTMR_SetPrescaler(base, prescVal);
	LPTMR_SetBypass(base, prescBypass);
	LPTMR_SetClockSelect(base, config->clockSelect);
	LPTMR_SetCompareValue(base, cmpValueTicks);
	LPTMR_SetPinSelect(base, config->pinSelect);
	LPTMR_SetPinPolarity(base, config->pinPolarity);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_GetConfig
 * Description   : Get the current configuration of the LPTMR instance.
 * Always returns compareValue in LPTMR_COUNTER_UNITS_TICKS.
 *
 * Implements : LPTMR_DRV_GetConfig_Activity
 *END**************************************************************************/
void LPTMR_DRV_GetConfig(const uint32_t instance, lptmr_config_t * const config) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);
	DEV_ASSERT(config != NULL);

	const LPTMR_Type* const base = g_lptmrBase[instance];

	/* Read current configuration */
	config->dmaRequest = LPTMR_GetDmaRequest(base);
	config->interruptEnable = LPTMR_GetInterruptEnable(base);
	config->freeRun = LPTMR_GetFreeRunning(base);
	config->workMode = LPTMR_GetWorkMode(base);
	config->prescaler = LPTMR_GetPrescaler(base);
	config->bypassPrescaler = LPTMR_GetBypass(base);
	config->clockSelect = LPTMR_GetClockSelect(base);
	config->compareValue = LPTMR_GetCompareValue(base);
	config->counterUnits = LPTMR_COUNTER_UNITS_TICKS;
	config->pinSelect = LPTMR_GetPinSelect(base);
	config->pinPolarity = LPTMR_GetPinPolarity(base);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_Deinit
 * Description   : De-initialize the LPTMR (stop the counter and reset all registers to default value).
 *
 * Implements : LPTMR_DRV_Deinit_Activity
 *END**************************************************************************/
void LPTMR_DRV_Deinit(const uint32_t instance) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	LPTMR_Type* const base = g_lptmrBase[instance];
	LPTMR_Disable(base);

	LPTMR_Init(base);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_SetCompareValueByCount
 * Description   : Set the compare value in counter tick units, for a LPTMR instance.
 * Possible return values:
 * - STATUS_SUCCESS: completed successfully
 * - STATUS_ERROR: cannot reconfigure compare value (TCF not set)
 * - STATUS_TIMEOUT: compare value is smaller than current counter value
 *
 * Implements : LPTMR_DRV_SetCompareValueByCount_Activity
 *END**************************************************************************/
status_t LPTMR_DRV_SetCompareValueByCount(const uint32_t instance,
		const uint16_t compareValueByCount) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	LPTMR_Type* const base = g_lptmrBase[instance];
	status_t statusCode = STATUS_SUCCESS;

	bool timerEnabled = LPTMR_GetEnable(base);
	bool compareFlag = LPTMR_GetCompareFlag(base);

	uint16_t counterVal;

	/* Check if a valid clock is selected for the timer/glitch filter */
#if (defined (DEV_ERROR_DETECT) || defined (CUSTOM_DEVASSERT))
	bool bypass = LPTMR_GetBypass(base);
	lptmr_workmode_t workMode = LPTMR_GetWorkMode(base);
	(void) bypass;
	(void) workMode;
#endif /* (defined (DEV_ERROR_DETECT) || defined (CUSTOM_DEVASSERT)) */
	DEV_ASSERT(
			(lptmr_GetClkFreq(LPTMR_GetClockSelect(base), instance) != 0u) || (bypass && (workMode == LPTMR_WORKMODE_PULSECOUNTER)));

	/* The compare value can only be written if counter is disabled or the compare flag is set. */
	if (timerEnabled && !compareFlag) {
		statusCode = STATUS_ERROR;
	} else {
		/* Check if new value is below the current counter value */
		LPTMR_SetCompareValue(base, compareValueByCount);
		counterVal = LPTMR_GetCounterValue(base);
		if (counterVal >= compareValueByCount) {
			statusCode = STATUS_TIMEOUT;
		}
	}

	return statusCode;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_GetCompareValueByCount
 * Description   : Get the compare value of timer in ticks units.
 *
 * Implements : LPTMR_DRV_GetCompareValueByCount_Activity
 *END**************************************************************************/
void LPTMR_DRV_GetCompareValueByCount(const uint32_t instance,
		uint16_t * const compareValueByCount) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	const LPTMR_Type* const base = g_lptmrBase[instance];

	*compareValueByCount = LPTMR_GetCompareValue(base);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_SetCompareValueUs
 * Description   : Set the compare value for Timer Mode in microseconds,
 * for a LPTMR instance.
 * Can be used only in Timer Mode.
 * Possible return values:
 * - STATUS_SUCCESS: completed successfully
 * - STATUS_ERROR: cannot reconfigure compare value
 * - STATUS_TIMEOUT: compare value greater then current counter value
 *
 * Implements : LPTMR_DRV_SetCompareValueByUs_Activity
 *END**************************************************************************/
status_t LPTMR_DRV_SetCompareValueByUs(const uint32_t instance,
		const uint32_t compareValueUs) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	status_t returnCode = STATUS_SUCCESS;
	LPTMR_Type* const base = g_lptmrBase[instance];
	bool timerEnabled, compareFlag;

	lptmr_clocksource_t clkSrc;
	uint32_t clkFreq;
	uint16_t cmpValTicks, currentCounterVal;
	lptmr_prescaler_t prescVal;
	bool prescBypass, conversionStatus;

	/* This function can only be used if LPTMR is configured in Timer Mode. */
	DEV_ASSERT(LPTMR_GetWorkMode(base) == LPTMR_WORKMODE_TIMER);

	timerEnabled = LPTMR_GetEnable(base);
	compareFlag = LPTMR_GetCompareFlag(base);
	/* The compare value can only be written if counter is disabled or the compare flag is set. */
	if (timerEnabled && !compareFlag) {
		returnCode = STATUS_ERROR;
	} else {
		clkSrc = LPTMR_GetClockSelect(base);
		clkFreq = lptmr_GetClkFreq(clkSrc, instance);
		DEV_ASSERT(clkFreq != 0U); /* Check the calculated clock frequency: '0' - invalid*/

		/* Get prescaler value and prescaler bypass state.*/
		prescVal = LPTMR_GetPrescaler(base);
		prescBypass = LPTMR_GetBypass(base);
		/* Convert new compare value from microseconds to ticks. */
		conversionStatus = lptmr_Us2Ticks(clkFreq, prescVal, prescBypass,
				compareValueUs, &cmpValTicks);
		DEV_ASSERT(conversionStatus == true); /* Check the conversion status: compareValueUs doesn't fit for current prescaller. */
		(void) conversionStatus;

		/* Write value and check if written successfully */
		LPTMR_SetCompareValue(base, cmpValTicks);
		currentCounterVal = LPTMR_GetCounterValue(base);

		if (currentCounterVal >= cmpValTicks) {
			returnCode = STATUS_TIMEOUT;
		}
	}

	return returnCode;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_GetCompareValueByUs
 * Description   : Get the compare value in microseconds representation.
 * Can be used only in Timer Mode.
 *
 * Implements : LPTMR_DRV_GetCompareValueByUs_Activity
 *END**************************************************************************/
void LPTMR_DRV_GetCompareValueByUs(const uint32_t instance,
		uint32_t * const compareValueUs) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);
	DEV_ASSERT(compareValueUs != NULL);

	const LPTMR_Type* const base = g_lptmrBase[instance];
	lptmr_clocksource_t clkSrc;
	uint32_t clkFreq;
	uint16_t cmpValTicks;
	lptmr_prescaler_t prescVal;
	bool prescBypass, conversionStatus;

	/* This function can only be used if LPTMR is configured in Timer Mode. */
	DEV_ASSERT(LPTMR_GetWorkMode(base) == LPTMR_WORKMODE_TIMER);

	clkSrc = LPTMR_GetClockSelect(base);
	clkFreq = lptmr_GetClkFreq(clkSrc, instance);
	/* The clock frequency must be valid. */
	DEV_ASSERT(clkFreq != 0U);

	/* Get prescaler value and prescaler bypass state.*/
	prescVal = LPTMR_GetPrescaler(base);
	prescBypass = LPTMR_GetBypass(base);
	cmpValTicks = LPTMR_GetCompareValue(base);

	/* Convert current compare value from ticks to microseconds. */
	conversionStatus = lptmr_Ticks2Us(clkFreq, prescVal, prescBypass,
			cmpValTicks, compareValueUs);
	DEV_ASSERT(conversionStatus == true); /* Check the conversion status. */
	(void) conversionStatus;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_GetCompareFlag
 * Description   : Get the current state of the Compare Flag of a LPTMR instance
 *
 * Implements : LPTMR_DRV_GetCompareFlag_Activity
 *END**************************************************************************/
bool LPTMR_DRV_GetCompareFlag(const uint32_t instance) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	const LPTMR_Type* const base = g_lptmrBase[instance];
	bool compareFlag = LPTMR_GetCompareFlag(base);

	return compareFlag;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_ClearCompareFlag
 * Description   : Clear the Compare Flag.
 *
 * Implements : LPTMR_DRV_ClearCompareFlag_Activity
 *END**************************************************************************/
void LPTMR_DRV_ClearCompareFlag(const uint32_t instance) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	LPTMR_Type* const base = g_lptmrBase[instance];

	LPTMR_ClearCompareFlag(base);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_IsRunning
 * Description   : Get the running state of a LPTMR instance.
 * Possible return values:
 * - true: Timer/Counter started
 * - false: Timer/Counter stopped
 *
 * Implements : LPTMR_DRV_IsRunning_Activity
 *END**************************************************************************/
bool LPTMR_DRV_IsRunning(const uint32_t instance) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	const LPTMR_Type* const base = g_lptmrBase[instance];

	bool runningState = LPTMR_GetEnable(base);

	return runningState;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_SetInterrupt
 * Description   : Enable/disable the LPTMR interrupt.
 *
 * Implements : LPTMR_DRV_SetInterrupt_Activity
 *END**************************************************************************/
void LPTMR_DRV_SetInterrupt(const uint32_t instance, const bool enableInterrupt) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	LPTMR_Type* const base = g_lptmrBase[instance];

	LPTMR_SetInterrupt(base, enableInterrupt);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_GetCounterValueTicks
 * Description   : Get the current Counter Value in timer ticks representation.
 * Return:
 *  - the counter value.
 *
 * Implements : LPTMR_DRV_GetCounterValueByCount_Activity
 *END**************************************************************************/
uint16_t LPTMR_DRV_GetCounterValueByCount(const uint32_t instance) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	LPTMR_Type* const base = g_lptmrBase[instance];

	uint16_t counterVal = LPTMR_GetCounterValue(base);

	return counterVal;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_StartCounter
 * Description   : Enable (start) the counter.
 *
 * Implements : LPTMR_DRV_StartCounter_Activity
 *END**************************************************************************/
void LPTMR_DRV_StartCounter(const uint32_t instance) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	LPTMR_Type* const base = g_lptmrBase[instance];

	/* Check if a valid clock is selected for the timer/glitch filter */
#if (defined (DEV_ERROR_DETECT) || defined (CUSTOM_DEVASSERT))
	bool bypass = LPTMR_GetBypass(base);
	lptmr_workmode_t workMode = LPTMR_GetWorkMode(base);
	(void) bypass;
	(void) workMode;
#endif /* (defined (DEV_ERROR_DETECT) || defined (CUSTOM_DEVASSERT)) */
	DEV_ASSERT(
			(lptmr_GetClkFreq(LPTMR_GetClockSelect(base), instance) != 0u) || (bypass && (workMode == LPTMR_WORKMODE_PULSECOUNTER)));

	LPTMR_Enable(base);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_StopCounter
 * Description   : Disable (stop) the counter.
 *
 * Implements : LPTMR_DRV_StopCounter_Activity
 *END**************************************************************************/
void LPTMR_DRV_StopCounter(const uint32_t instance) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	LPTMR_Type* const base = g_lptmrBase[instance];

	LPTMR_Disable(base);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : LPTMR_DRV_SetPinConfiguration
 * Description   : Set the Input Pin configuration for Pulse Counter mode.
 *
 * Implements : LPTMR_DRV_SetPinConfiguration_Activity
 *END**************************************************************************/
void LPTMR_DRV_SetPinConfiguration(const uint32_t instance,
		const lptmr_pinselect_t pinSelect,
		const lptmr_pinpolarity_t pinPolarity) {
	DEV_ASSERT(instance < LPTMR_INSTANCE_COUNT);

	LPTMR_Type* const base = g_lptmrBase[instance];

	LPTMR_SetPinSelect(base, pinSelect);
	LPTMR_SetPinPolarity(base, pinPolarity);
}

/*******************************************************************************
 * EOF
 ******************************************************************************/
